package current.voltails;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 性能更高的double-checked单例
 * 双重检查锁定模式(Double checked locking)是软件设计的小技巧，
 * 第一重检查跳过大多数不需要竞争的情况，从而减少并发系统中的竞争开销。
 */
public class DoubleCheck {
    static class DbConnection {
        DbConnection() {
            System.out.println("Thread + " + Thread.currentThread().getId() + " is initializing");
        }
    }
    /**
     * 指令1
     * 指令2
     * acquire/release/volatile
     * 指令3
     * 指令4
     */
    // volatile不允许之前、之后任何指令的重排序(强约束)，对性能有损耗
    // static volatile DbConnection ref;
    // 使用速度更快的AtomicReference，约束更轻(weak)，策略更多
    static AtomicReference<DbConnection> ref = new AtomicReference<>();

    public static DbConnection getConnection() {
        // AtomicReference.getAcquire在JDK9引入，具有happens-before约束，
        // 确保在它之前的指令虽然可以指令重排但一定发生在它之前，它之后的指令虽然可以指令重排但一定发生在它之后
        DbConnection localRef = ref.getAcquire();
        if (Objects.isNull(localRef)) {
            synchronized (DoubleCheck.class) {
                System.out.println("synchronized拦截通过的线程：" + Thread.currentThread().getId());
                localRef = ref.getAcquire();
                if (Objects.isNull(localRef)) {
                    // 构造函数的构造一半的问题，构造时间比较长的情况下可能指令重排，
                    // 可能在构造一半的情况下对ref进行了赋值，ref = new DbConnection()之间没有强的happens-before约束
                    // 这里可以使用volatile关键字
                    // ref = new DbConnection();
                    ref.setRelease(new DbConnection());
                }
            }
        }
        return localRef;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 12; i++) {
            new Thread(DoubleCheck::getConnection).start();
        }
    }
}
